﻿/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections.Generic;
using System.IO;
using STSdb.General.Collections;
using STSdb.General.Compression;
using STSdb.General.Mathematics;

namespace STSdb.Persist
{
    public class SingleDeltaIndexerPersist : IIndexerPersist<Single>
    {
        private int GetMaxDigits(IIndexer<float> values)
        {
            int maxDigits = 0;
            for (int i = 0; i < values.Count; i++)
            {
                float value = values[i];
                int digits = MathUtils.GetDigits(value);
                if (digits < 0)
                    return -1;

                if (digits > maxDigits)
                    maxDigits = digits;
            }
            return maxDigits;
        }

        #region IIndexerPersist<float> Members

        public void Store(BinaryWriter writer, IIndexer<float> values)
        {
            DeltaCompression.Helper helper = null;
            List<long> rawValues = null;
            int maxDigits;

            try
            {
                maxDigits = GetMaxDigits(values);
                if (maxDigits >= 0)
                {
                    helper = new DeltaCompression.Helper();
                    rawValues = new List<long>(values.Count);

                    double koef = Math.Pow(10, maxDigits);
                    for (int i = 0; i < values.Count; i++)
                    {
                        float value = values[i];
                        long v = checked((long)Math.Round(value * koef));
                        helper.AddValue(v);
                        rawValues.Add(v);
                    }
                }
            }
            catch (OverflowException)
            {
                maxDigits = -1;
            }

            writer.Write((sbyte)maxDigits);
            if (maxDigits >= 0)
                DeltaCompression.CoreCompress(writer, rawValues, helper);
            else
            {
                for (int i = 0; i < values.Count; i++)
                    writer.Write(values[i]);
            }
        }

        public void Load(BinaryReader reader, IIndexer<float> values)
        {
            int digits = reader.ReadSByte();
            if (digits >= 0)
            {
                double koef = Math.Pow(10, digits);
                List<long> rawValues = (List<long>)DeltaCompression.CoreDecompress(reader);
                for (int i = 0; i < values.Count; i++)
                    values[i] = (float)Math.Round(rawValues[i] / koef, digits);
            }
            else //native read
            {
                for (int i = 0; i < values.Count; i++)
                    values[i] = reader.ReadSingle();
            }
        }

        #endregion
    }

    public class DoubleDeltaIndexerPersist : IIndexerPersist<Double>
    {
        private int GetMaxDigits(IIndexer<Double> values)
        {
            int maxDigits = 0;
            for (int i = 0; i < values.Count; i++)
            {
                double value = values[i];
                int digits = MathUtils.GetDigits(value);
                if (digits < 0)
                    return -1;

                if (digits > maxDigits)
                    maxDigits = digits;
            }
            return maxDigits;
        }

        #region IIndexerPersist<double> Members

        public void Store(BinaryWriter writer, IIndexer<double> values)
        {
            DeltaCompression.Helper helper = null;
            List<long> rawValues = null;
            int maxDigits;

            try
            {
                maxDigits = GetMaxDigits(values);
                if (maxDigits >= 0)
                {
                    helper = new DeltaCompression.Helper();
                    rawValues = new List<long>(values.Count);

                    double koef = Math.Pow(10, maxDigits);
                    for (int i = 0; i < values.Count; i++)
                    {
                        double value = values[i];
                        long v = checked((long)Math.Round(value * koef));
                        helper.AddValue(v);
                        rawValues.Add(v);
                    }
                }
            }
            catch (OverflowException)
            {
                maxDigits = -1;
            }

            writer.Write((sbyte)maxDigits);
            if (maxDigits >= 0)
                DeltaCompression.CoreCompress(writer, rawValues, helper);
            else
            {
                for (int i = 0; i < values.Count; i++)
                    writer.Write(values[i]);
            }
        }

        public void Load(BinaryReader reader, IIndexer<double> values)
        {
            int digits = reader.ReadSByte();
            if (digits >= 0)
            {
                double koef = Math.Pow(10, digits);
                List<long> rawValues = (List<long>)DeltaCompression.CoreDecompress(reader);
                for (int i = 0; i < values.Count; i++)
                    values[i] = (double)Math.Round(rawValues[i] / koef, digits);
            }
            else //native read
            {
                for (int i = 0; i < values.Count; i++)
                    values[i] = reader.ReadDouble();
            }
        }

        #endregion
    }

    public class DecimalDeltaIndexerPersist : IIndexerPersist<Decimal>
    {
        private int GetMaxDigits(IIndexer<Decimal> values)
        {
            int maxDigits = 0;
            for (int i = 0; i < values.Count; i++)
            {
                decimal value = values[i];
                int digits = MathUtils.GetDigits(value);
                if (digits > maxDigits)
                    maxDigits = digits;
            }
            return maxDigits;
        }

        #region IIndexerPersist<decimal> Members

        public void Store(BinaryWriter writer, IIndexer<decimal> values)
        {
            DeltaCompression.Helper helper = null;
            List<long> rawValues = null;
            int maxDigits;

            try
            {
                maxDigits = GetMaxDigits(values);
                if (maxDigits <= 15)
                {
                    helper = new DeltaCompression.Helper();
                    rawValues = new List<long>(values.Count);

                    decimal koef = (decimal)Math.Pow(10, maxDigits);
                    for (int i = 0; i < values.Count; i++)
                    {
                        decimal value = values[i];
                        long v = checked((long)Math.Round(value * koef));
                        helper.AddValue(v);
                        rawValues.Add(v);
                    }
                }
                else
                    maxDigits = -1;
            }
            catch (OverflowException)
            {
                maxDigits = -1;
            }

            writer.Write((sbyte)maxDigits);
            if (maxDigits >= 0)
                DeltaCompression.CoreCompress(writer, rawValues, helper);
            else
            {
                for (int i = 0; i < values.Count; i++)
                    writer.Write(values[i]);
            }
        }

        public void Load(BinaryReader reader, IIndexer<decimal> values)
        {
            int digits = reader.ReadSByte();
            if (digits >= 0)
            {
                double koef = Math.Pow(10, digits);
                List<long> rawValues = (List<long>)DeltaCompression.CoreDecompress(reader);
                for (int i = 0; i < values.Count; i++)
                    values[i] = (decimal)Math.Round(rawValues[i] / koef, digits);
            }
            else //native read
            {
                for (int i = 0; i < values.Count; i++)
                    values[i] = reader.ReadDecimal();
            }
        }

        #endregion
    }
}
